using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ParserGenerator.Extensions;

namespace ParserGenerator.CharacterMapping
{
    [Serializable]
    public sealed class CharClassBuilder
    {
        public CharInterval Universal { get; }

        [NonSerialized]
        private ReadOnlyCollection<CharInterval> _intervalsWrapper;
        private readonly List<CharInterval> _intervals;
        public ReadOnlyCollection<CharInterval> Intervals
        {
            get
            {
                if (_intervalsWrapper == null)
                    _intervalsWrapper = _intervals.AsReadOnly();
                return _intervalsWrapper;
            }
        }

        public CharClassBuilder()
            : this(CharInterval.Universal)
        {
        }

        public CharClassBuilder(CharInterval universal)
        {
            Universal = universal;
            _intervals = new List<CharInterval>();
        }

        public void Add(int first, int last)
        {
            if (!Universal.Contains(first))
                throw new ArgumentOutOfRangeException(nameof(first), string.Format(SR.CharMustBeInRange, Universal));
            if (!Universal.Contains(last))
                throw new ArgumentOutOfRangeException(nameof(last), string.Format(SR.CharMustBeInRange, Universal));
            if (first > last)
                throw new ArgumentException(SR.FirstLastNotRange);
            _intervals.Add(new CharInterval(first, last));
        }

        public void Add(int c)
        {
            if (!Universal.Contains(c))
                throw new ArgumentOutOfRangeException(nameof(c), string.Format(SR.CharMustBeInRange, Universal));
            _intervals.Add(new CharInterval(c));
        }

        public void Add(CharInterval item)
        {
            if (!Universal.Includes(item))
                throw new ArgumentOutOfRangeException(nameof(item), string.Format(SR.CharMustBeInRange, Universal));
            _intervals.Add(item);
        }

        public void AddRange(IEnumerable<CharInterval> collection)
        {
            if (collection == null)
                throw new ArgumentNullException(nameof(collection));
            foreach (CharInterval item in collection)
                if (!Universal.Includes(item))
                    throw new ArgumentOutOfRangeException(nameof(item), string.Format(SR.CharMustBeInRange, Universal));
            _intervals.AddRange(collection);
        }

        public void Clear()
        {
            _intervals.Clear();
        }

        public CharClass Build(bool negated)
        {
            List<CharInterval> items = new List<CharInterval>(_intervals);
            CharIntervalHelpers.Normalize(items);
            if (negated)
                CharIntervalHelpers.Negate(items, Universal);
            return new CharClass(null, Universal, items.ToArray().AsReadOnly());
        }

        public CharClass Build()
        {
            return Build(false);
        }

        public sealed override string ToString()
        {
            return CharIntervalHelpers.FormatClass(_intervals, Universal);
        }
    }
}
